package com.gitee.wenbo0;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.gitee.wenbo0.annotation.*;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Consumer;

/**
 * @author wenbo
 * @since 2022/5/10 14:51
 */
public class WrapperUtil {
    public static <T> QueryWrapper<T> buildQueryWrapper(T entity) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        Class<?> aClass = entity.getClass();
        WrapperGroups wrapperGroups = aClass.getAnnotation(WrapperGroups.class);
        Field[] fields = aClass.getDeclaredFields();
        Map<Wrapper, List<Field>> fieldMap = new LinkedHashMap<>();
        for (Field field : fields) {
            field.setAccessible(true);
            Wrapper wrapper = field.getAnnotation(Wrapper.class);
            if (wrapper == null)
                continue;
            try {
                Object value = field.get(entity);
                String columnName;
                TableField tableField = field.getAnnotation(TableField.class);
                if (tableField != null && tableField.exist() && StringUtils.isNotBlank(tableField.value())) {
                    columnName = tableField.value();
                } else {
                    columnName = getColumnName(field, wrapper);
                }
                boolean condition = isCondition(wrapper, value);
                if (StringUtils.isBlank(wrapper.group())) {
                    invokeMethod(queryWrapper, wrapper, value, columnName, condition);
                } else {
                    List<Field> fieldList = fieldMap.get(wrapper);
                    if (fieldList == null) {
                        fieldMap.put(wrapper, new LinkedList<Field>() {{
                            add(field);
                        }});
                    } else {
                        fieldList.add(field);
                        fieldMap.put(wrapper, fieldList);
                    }
                }
            } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                e.printStackTrace();
            }
            field.setAccessible(false);
        }
        if (wrapperGroups != null && wrapperGroups.groups().length > 0) {
            for (Group group : wrapperGroups.groups()) {
                long count = Arrays.stream(fields).filter(field1 ->
                        field1.getAnnotation(Wrapper.class) != null
                                && group.group().equals(field1.getAnnotation(Wrapper.class).group())).count();
                if (count == 0) {
                    continue;
                }
                long c = Arrays.stream(fields).filter(field -> {
                    try {
                        field.setAccessible(true);
                        boolean condition = isCondition(field.getAnnotation(Wrapper.class), field.get(entity));
                        field.setAccessible(false);
                        return condition;
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }).count();
                if (c > 0) {
                    Consumer<QueryWrapper<T>> consumer = i -> {
                        for (int j = 0, fieldsLength = fields.length; j < fieldsLength; j++) {
                            Field field = fields[j];
                            Wrapper wrapper = field.getAnnotation(Wrapper.class);
                            if (wrapper != null && group.group().equals(wrapper.group())) {
                                try {
                                    field.setAccessible(true);
                                    Object value = field.get(entity);
                                    invokeMethod(i, wrapper, value, getColumnName(field, wrapper), isCondition(wrapper, value));
                                    field.setAccessible(false);
                                    if (group.conditionType() == ConditionType.OR && j < fieldsLength - 1)
                                        i.or();
                                } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    };
                    if (group.splicingType() == ConditionType.AND) {
                        queryWrapper.and(consumer);
                    } else if (group.splicingType() == ConditionType.OR) {
                        queryWrapper.or(consumer);
                    }
                }
            }
        }
        return queryWrapper;
    }

    private static boolean isCondition(Wrapper wrapper, Object value) {
        if (wrapper == null) return false;
        boolean ignoreNull = wrapper.ignoreNull();
        boolean ignoreBlank = wrapper.ignoreBlank();
        return getCondition(ignoreNull, ignoreBlank, value);
    }

    private static String getColumnName(Field field, Wrapper wrapper) {
        return StringUtils.isNotBlank(wrapper.columnName())
                ? wrapper.columnName()
                : StringUtils.camelToUnderline(field.getName());
    }

    private static <T> void invokeMethod(QueryWrapper<T> queryWrapper, Wrapper annotation, Object value, String columnName, boolean condition) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        Method method;
        if (annotation.value() == WrapperType.IN || annotation.value() == WrapperType.NOT_IN) {
            method = queryWrapper.getClass().getMethod(StringUtils.underlineToCamel(annotation.value().toString()),
                    boolean.class, Object.class, Collection.class);
            if (value instanceof String[]) {
                method.invoke(queryWrapper, condition, columnName, Arrays.asList((String[]) value));
            } else if (value instanceof String) {
                method.invoke(queryWrapper, condition, columnName, Arrays.asList(value.toString().split(",")));
            }
        } else {
            method = queryWrapper.getClass().getMethod(StringUtils.underlineToCamel(annotation.value().toString()),
                    boolean.class, Object.class, Object.class);
            method.invoke(queryWrapper, condition, columnName, value);
        }
    }

    private static boolean getCondition(boolean ignoreNull, boolean ignoreBlank, Object value) {
        if (value == null) {
            return !ignoreNull;
        } else {
            if (value instanceof String) {
                if (StringUtils.isBlank((String) value)) {
                    return !ignoreBlank;
                }
            }
            return true;
        }
    }
}
